/**
 * Copyright © 2011 Creativor Studio.
 * About license information, please see LICENSE.txt.
 */
package org.creativor.rayson.server;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import org.creativor.rayson.api.RpcProtocol;
import org.creativor.rayson.api.RpcService;
import org.creativor.rayson.api.Session;
import org.creativor.rayson.common.Invocation;
import org.creativor.rayson.common.RpcCallException;
import org.creativor.rayson.exception.CallInvokeException;
import org.creativor.rayson.exception.IllegalMethodException;
import org.creativor.rayson.exception.IllegalServiceException;
import org.creativor.rayson.exception.ServiceNotFoundException;
import org.creativor.rayson.util.ProxyVerifier;

// TODO: Auto-generated Javadoc

/**
 * A object that reflection the java reflect of one {@link RpcService}.
 * 
 * @author Nick Zhang
 */
class ServiceReflection
{
	private String description;
	private RpcService instance;
	private String name;
	/**
	 * Map of <Proxy method hash code ==> service interface method>
	 */
	private HashMap<Integer, Method> methods;
	private Class<? extends RpcProtocol>[] protocols;
	
	private static class ProtocolMethod
	{
		private Method method;
		
		/**
		 * @param method
		 * @throws IllegalMethodException
		 */
		ProtocolMethod(Method method) throws IllegalMethodException
		{
			this.method = method;
			ProxyVerifier.verifyProxyMethod(method);
		}
		
		public Method getMethod()
		{
			return method;
		}
		
	}
	
	/**
	 * Instantiates a new service reflection.
	 * 
	 * @param name
	 *            the name
	 * @param description
	 *            the description
	 * @param instance
	 *            the instance
	 * @throws IllegalServiceException
	 *             the illegal service exception
	 */
	ServiceReflection(String name, String description, RpcService instance)
			throws IllegalServiceException
	{
		if (name == null && description == null && instance == null)
			throw new IllegalArgumentException("All arguments should not be null");
		this.name = name;
		this.description = description;
		this.instance = instance;
		methods = new HashMap<Integer, Method>();
		ArrayList<Class<? extends RpcProtocol>> protocolList =
				new ArrayList<Class<? extends RpcProtocol>>();
		for (Class serviceInterface : instance.getClass().getInterfaces())
		{
			if (!RpcService.class.isAssignableFrom(serviceInterface)) continue;
			for (Class protocol : serviceInterface.getInterfaces())
			{
				if (!RpcProtocol.class.isAssignableFrom(protocol)) continue;
				protocolList.add(protocol);
			}
		}
		this.protocols = (Class<? extends RpcProtocol>[]) protocolList.toArray(new Class<?>[0]);
		for (Class<? extends RpcProtocol> protocol : protocolList)
		{
			for (Method method : protocol.getDeclaredMethods())
			{
				try
				{
					ProtocolMethod protocolMethod = new ProtocolMethod(method);
				} catch (IllegalMethodException e)
				{
					throw new IllegalServiceException("Protocol method " + method.toString()
							+ " is illegal one: " + e.getMessage());
				}
				this.methods.put(Invocation.getHashCode(method), method);
			}
		}
	}
	
	/**
	 * Gets the description.
	 * 
	 * @return the description
	 */
	public String getDescription()
	{
		return this.description;
	}
	
	/**
	 * Gets the single instance of ServiceReflection.
	 * 
	 * @return single instance of ServiceReflection
	 */
	public RpcService getInstance()
	{
		return instance;
	}
	
	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	public String getName()
	{
		return name;
	}
	
	/**
	 * Invoke.
	 * 
	 * @param session
	 *            the request
	 * @param invocation
	 *            the invocation
	 * @return the object
	 * @throws RpcCallException
	 *             the rpc call exception
	 */
	public Object invoke(Session session, Invocation invocation) throws RpcCallException
	{
		Method method = methods.get(invocation.getHashCode());
		if (method == null)
			throw new RpcCallException(new ServiceNotFoundException("service of "
					+ session.getServiceName() + " hash code " + invocation.getHashCode()
					+ " method " + " not found"));
		
		try
		{
			return invocation.invoke(this.instance, method);
		} catch (CallInvokeException e)
		{
			throw new RpcCallException(e);
		}
	}
	
	/**
	 * Return {@link RpcProtocol} list define in this RPC service.
	 */
	public Class<? extends RpcProtocol>[] getProtocols()
	{
		return protocols;
	}
}
